package com.mxchen.lottery.domain.strategy.service.draw;

import com.mxchen.lottery.client.activity.api.IDrawExec;
import com.mxchen.lottery.client.activity.dto.DrawCmd;
import com.mxchen.lottery.client.activity.dto.DrawDTO;
import com.mxchen.lottery.client.common.Constants;
import com.mxchen.lottery.domain.strategy.model.AwardDetails;
import com.mxchen.lottery.domain.strategy.model.AwardRateInfo;
import com.mxchen.lottery.domain.strategy.model.StrategyDetails;
import com.mxchen.lottery.domain.strategy.service.algorithm.IDrawAlgorithm;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

/**
 * @author : hmxchen
 * @program : lottery-964-mxchen
 * @description :
 * 定义抽象抽奖过程，模板模式
 * @create : 2021-12-17 09:38
 **/
@Slf4j
public abstract class AbstractDrawBase extends DrawStrategySupport  implements IDrawExec {
    @Override
    public DrawDTO doDrawExec(DrawCmd cmd) {
        log.info("执行策略抽奖开始，strategyId：{}", cmd.getStrategyId());
        // 获取抽奖策略配置数据
        StrategyDetails strategyRich = super.queryStrategyRich(cmd.getStrategyId());
        // 校验和初始化数据
        this.checkAndInitRateData(strategyRich.getStrategyId(), strategyRich.getStrategyMode(),strategyRich.getStrategyDetailList());
        // 3. 获取不在抽奖范围内的列表，包括：奖品库存为空、风控策略、临时调整等
        List<String> excludeAwardIds = this.queryExcludeAwardIds(cmd.getStrategyId());
        // 4. 执行抽奖算法
        String awardId = this.drawAlgorithm(cmd.getStrategyId(), drawAlgorithmMap.get(strategyRich.getStrategyMode()), excludeAwardIds);
        // 5. 包装中奖结果
        return buildDrawResult(cmd.getUId(), cmd.getStrategyId(), awardId);
    }

    /**
     * 获取不在抽奖范围内的列表，包括：奖品库存为空、风控策略、临时调整等，这类数据是含有业务逻辑的，所以需要由具体的实现方决定
     *
     * @param strategyId 策略ID
     * @return 排除的奖品ID集合
     */
    protected abstract List<String> queryExcludeAwardIds(Long strategyId);

    /**
     * 执行抽奖算法
     *
     * @param strategyId      策略ID
     * @param drawAlgorithm   抽奖算法模型
     * @param excludeAwardIds 排除的抽奖ID集合
     * @return 中奖奖品ID
     */
    protected abstract String drawAlgorithm(Long strategyId, IDrawAlgorithm drawAlgorithm, List<String> excludeAwardIds);

    public void checkAndInitRateData(Long strategyId, Integer strategyMode, List<AwardDetails> strategyDetailList) {
        // 非单项概率，不必存入缓存
        if (!Constants.StrategyMode.SINGLE.getCode().equals(strategyMode)) {
            return;
        }

        IDrawAlgorithm drawAlgorithm = drawAlgorithmMap.get(strategyMode);

        // 已初始化过的数据，不必重复初始化
        if (drawAlgorithm.isExistRateTuple(strategyId)) {
            return;
        }
        List<AwardRateInfo> awardRateInfoList = new ArrayList<>(strategyDetailList.size());
        for (AwardDetails strategyDetail : strategyDetailList) {
            awardRateInfoList.add(new AwardRateInfo(strategyDetail.getAwardId(), strategyDetail.getAwardRate()));
        }
        drawAlgorithm.initRateTuple(strategyId, awardRateInfoList);
    }
    /**
     * 包装抽奖结果
     *
     * @param uId        用户ID
     * @param strategyId 策略ID
     * @param awardId    奖品ID，null 情况：并发抽奖情况下，库存临界值1 -> 0，会有用户中奖结果为 null
     * @return 中奖结果
     */
    private DrawDTO buildDrawResult(String uId, Long strategyId, String awardId) {
        if (null == awardId) {
            log.info("执行策略抽奖完成【未中奖】，用户：{} 策略ID：{}", uId, strategyId);
            return DrawDTO.createFail(uId, strategyId);
        }
        // 获取奖品信息
        AwardDetails awardDetails = super.queryAwardInfoByAwardId(awardId);
        return DrawDTO.createSuccess(uId,strategyId,awardId,awardDetails.getAwardName());
    }
}
